package com.srmt.common.ratelimit.utils;

import com.srmt.common.ratelimit.RateLimiter;
import com.srmt.common.ratelimit.config.RateLimitConfig;
import com.srmt.common.ratelimit.impl.LocalRateLimiter;
import com.srmt.common.ratelimit.impl.GuavaRateLimiter;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 限流测试工具类
 * 提供常用的测试辅助方法
 * 
 * @author SRMT Development Team
 * @since 1.0.0
 */
public class RateLimitTestUtils {
    
    /**
     * 限流器类型枚举
     */
    public enum LimiterType {
        LOCAL("LocalRateLimiter"),
        GUAVA("GuavaRateLimiter");
        
        private final String displayName;
        
        LimiterType(String displayName) {
            this.displayName = displayName;
        }
        
        public String getDisplayName() {
            return displayName;
        }
    }
    
    /**
     * 测试结果类
     */
    public static class TestResult {
        public final int totalRequests;
        public final int successCount;
        public final int failureCount;
        public final long duration;
        public final double actualQps;
        public final double successRate;
        public final List<Long> latencies;
        public final Map<String, Object> additionalMetrics;
        
        public TestResult(int totalRequests, int successCount, int failureCount, 
                         long duration, List<Long> latencies) {
            this.totalRequests = totalRequests;
            this.successCount = successCount;
            this.failureCount = failureCount;
            this.duration = duration;
            this.actualQps = (successCount * 1000.0) / duration;
            this.successRate = (successCount * 100.0) / totalRequests;
            this.latencies = new ArrayList<>(latencies);
            this.additionalMetrics = new HashMap<>();
        }
        
        public double getAverageLatency() {
            return latencies.stream()
                .mapToLong(Long::longValue)
                .average()
                .orElse(0);
        }
        
        public long getP50Latency() {
            return getPercentile(50);
        }
        
        public long getP95Latency() {
            return getPercentile(95);
        }
        
        public long getP99Latency() {
            return getPercentile(99);
        }
        
        private long getPercentile(int percentile) {
            if (latencies.isEmpty()) return 0;
            List<Long> sorted = new ArrayList<>(latencies);
            Collections.sort(sorted);
            int index = (sorted.size() * percentile) / 100;
            return sorted.get(Math.min(index, sorted.size() - 1));
        }
        
        @Override
        public String toString() {
            return String.format(
                "TestResult{requests=%d, success=%d, failure=%d, duration=%dms, " +
                "actualQps=%.2f, successRate=%.2f%%, avgLatency=%.2fms, p99Latency=%dms}",
                totalRequests, successCount, failureCount, duration,
                actualQps, successRate, getAverageLatency() / 1000.0, getP99Latency() / 1000
            );
        }
    }
    
    /**
     * 负载生成配置
     */
    public static class LoadConfig {
        public int threadCount = 10;
        public int requestsPerThread = 100;
        public long testDuration = 0; // 0表示基于请求数，>0表示基于时间
        public int requestDelayMs = 10;
        public int requestDelayVariance = 5;
        public boolean measureLatency = true;
        public Consumer<TestResult> progressCallback = null;
        
        public static LoadConfig defaultConfig() {
            return new LoadConfig();
        }
        
        public LoadConfig withThreads(int count) {
            this.threadCount = count;
            return this;
        }
        
        public LoadConfig withRequestsPerThread(int requests) {
            this.requestsPerThread = requests;
            return this;
        }
        
        public LoadConfig withDuration(long durationMs) {
            this.testDuration = durationMs;
            return this;
        }
        
        public LoadConfig withDelay(int delayMs, int variance) {
            this.requestDelayMs = delayMs;
            this.requestDelayVariance = variance;
            return this;
        }
    }
    
    /**
     * 创建限流器实例
     */
    public static RateLimiter createLimiter(LimiterType type, int defaultQps) {
        RateLimitConfig config = RateLimitConfig.builder()
            .defaultQps(defaultQps)
            .build();
            
        switch (type) {
            case LOCAL:
                return new LocalRateLimiter(config);
            case GUAVA:
                return new GuavaRateLimiter(config);
            default:
                throw new IllegalArgumentException("Unknown limiter type: " + type);
        }
    }
    
    /**
     * 生成负载并测试限流器
     */
    public static TestResult generateLoad(RateLimiter limiter, String api, LoadConfig config) 
            throws InterruptedException {
        
        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failureCount = new AtomicInteger(0);
        List<Long> latencies = Collections.synchronizedList(new ArrayList<>());
        
        ExecutorService executor = Executors.newFixedThreadPool(config.threadCount);
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch endLatch = new CountDownLatch(config.threadCount);
        
        long startTime = System.currentTimeMillis();
        
        // 基于时间的测试
        if (config.testDuration > 0) {
            for (int i = 0; i < config.threadCount; i++) {
                executor.submit(() -> {
                    try {
                        startLatch.await();
                        long endTime = System.currentTimeMillis() + config.testDuration;
                        
                        while (System.currentTimeMillis() < endTime) {
                            executeRequest(limiter, api, config, successCount, failureCount, latencies);
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    } finally {
                        endLatch.countDown();
                    }
                });
            }
        } 
        // 基于请求数的测试
        else {
            for (int i = 0; i < config.threadCount; i++) {
                executor.submit(() -> {
                    try {
                        startLatch.await();
                        
                        for (int j = 0; j < config.requestsPerThread; j++) {
                            executeRequest(limiter, api, config, successCount, failureCount, latencies);
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    } finally {
                        endLatch.countDown();
                    }
                });
            }
        }
        
        // 启动所有线程
        startLatch.countDown();
        
        // 等待完成
        endLatch.await();
        executor.shutdown();
        
        long duration = System.currentTimeMillis() - startTime;
        int totalRequests = successCount.get() + failureCount.get();
        
        TestResult result = new TestResult(
            totalRequests,
            successCount.get(),
            failureCount.get(),
            duration,
            latencies
        );
        
        // 回调进度
        if (config.progressCallback != null) {
            config.progressCallback.accept(result);
        }
        
        return result;
    }
    
    /**
     * 执行单个请求
     */
    private static void executeRequest(RateLimiter limiter, String api, LoadConfig config,
                                      AtomicInteger successCount, AtomicInteger failureCount,
                                      List<Long> latencies) throws InterruptedException {
        
        long startTime = config.measureLatency ? System.nanoTime() : 0;
        boolean acquired = limiter.tryAcquire(api, 10); // 10ms超时
        
        if (config.measureLatency) {
            long latency = System.nanoTime() - startTime;
            latencies.add(latency);
        }
        
        if (acquired) {
            successCount.incrementAndGet();
        } else {
            failureCount.incrementAndGet();
        }
        
        // 请求间延迟
        if (config.requestDelayMs > 0) {
            int delay = config.requestDelayMs;
            if (config.requestDelayVariance > 0) {
                delay += ThreadLocalRandom.current().nextInt(-config.requestDelayVariance, 
                                                            config.requestDelayVariance);
            }
            Thread.sleep(Math.max(1, delay));
        }
    }
    
    /**
     * 比较不同限流器的性能
     */
    public static Map<LimiterType, TestResult> compareLimiters(int qps, String api, 
                                                               LoadConfig config) 
            throws InterruptedException {
        
        Map<LimiterType, TestResult> results = new LinkedHashMap<>();
        
        for (LimiterType type : LimiterType.values()) {
            // 跳过可能未实现的限流器
            try {
                RateLimiter limiter = createLimiter(type, qps);
                limiter.setQps(api, qps);
                
                // 预热
                for (int i = 0; i < 10; i++) {
                    limiter.tryAcquire(api, 0);
                }
                
                // 测试
                TestResult result = generateLoad(limiter, api, config);
                results.put(type, result);
                
                // 清理
                limiter.shutdown();
                
                // 间隔一下避免影响
                Thread.sleep(1000);
                
            } catch (Exception e) {
                System.err.println("Failed to test " + type + ": " + e.getMessage());
            }
        }
        
        return results;
    }
    
    /**
     * 测量QPS精度
     */
    public static double measureQpsPrecision(RateLimiter limiter, String api, 
                                            int expectedQps, long durationMs) 
            throws InterruptedException {
        
        limiter.setQps(api, expectedQps);
        
        LoadConfig config = LoadConfig.defaultConfig()
            .withThreads(Math.min(expectedQps / 10 + 1, 20))
            .withDuration(durationMs)
            .withDelay(5, 3);
            
        TestResult result = generateLoad(limiter, api, config);
        
        double errorRate = Math.abs((result.actualQps - expectedQps) / (double) expectedQps) * 100;
        return errorRate;
    }
    
    /**
     * 生成测试报告
     */
    public static String generateReport(Map<LimiterType, TestResult> results) {
        StringBuilder report = new StringBuilder();
        report.append("\n========== 限流器性能对比报告 ==========\n\n");
        
        // 表头
        report.append(String.format("%-20s %-10s %-10s %-12s %-12s %-10s %-10s %-10s\n",
            "Limiter", "Requests", "Success", "Actual QPS", "Success Rate", 
            "Avg Latency", "P95", "P99"));
        report.append(createRepeatedString("-", 110)).append("\n");
        
        // 数据行
        for (Map.Entry<LimiterType, TestResult> entry : results.entrySet()) {
            TestResult result = entry.getValue();
            report.append(String.format("%-20s %-10d %-10d %-12.2f %-12.2f%% %-10.2fms %-10dμs %-10dμs\n",
                entry.getKey().getDisplayName(),
                result.totalRequests,
                result.successCount,
                result.actualQps,
                result.successRate,
                result.getAverageLatency() / 1_000_000.0,
                result.getP95Latency() / 1000,
                result.getP99Latency() / 1000
            ));
        }
        
        report.append("\n");
        
        // 找出最佳性能
        if (!results.isEmpty()) {
            LimiterType bestQpsAccuracy = results.entrySet().stream()
                .min(Comparator.comparing(e -> 
                    Math.abs(e.getValue().actualQps - e.getValue().totalRequests)))
                .map(Map.Entry::getKey)
                .orElse(null);
                
            LimiterType bestLatency = results.entrySet().stream()
                .min(Comparator.comparing(e -> e.getValue().getP99Latency()))
                .map(Map.Entry::getKey)
                .orElse(null);
                
            report.append("最佳QPS精度: ").append(bestQpsAccuracy).append("\n");
            report.append("最低延迟: ").append(bestLatency).append("\n");
        }
        
        return report.toString();
    }
    
    /**
     * 模拟突发流量
     */
    public static TestResult simulateBurst(RateLimiter limiter, String api, 
                                          int burstSize, int normalQps) 
            throws InterruptedException {
        
        limiter.setQps(api, normalQps);
        
        // 等待令牌积累
        Thread.sleep(2000);
        
        // 突发请求
        AtomicInteger successCount = new AtomicInteger(0);
        AtomicInteger failureCount = new AtomicInteger(0);
        List<Long> latencies = new ArrayList<>();
        
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < burstSize; i++) {
            long requestStart = System.nanoTime();
            boolean acquired = limiter.tryAcquire(api, 0);
            long latency = System.nanoTime() - requestStart;
            
            if (acquired) {
                successCount.incrementAndGet();
            } else {
                failureCount.incrementAndGet();
            }
            latencies.add(latency);
        }
        
        long duration = System.currentTimeMillis() - startTime;
        
        return new TestResult(burstSize, successCount.get(), failureCount.get(), duration, latencies);
    }
    
    /**
     * 等待条件满足或超时
     */
    public static boolean waitForCondition(Supplier<Boolean> condition, long timeoutMs) 
            throws InterruptedException {
        
        long deadline = System.currentTimeMillis() + timeoutMs;
        
        while (System.currentTimeMillis() < deadline) {
            if (condition.get()) {
                return true;
            }
            Thread.sleep(10);
        }
        
        return false;
    }

    /**
     * 创建重复字符串 - Java 8兼容版本
     */
    private static String createRepeatedString(String str, int count) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count; i++) {
            sb.append(str);
        }
        return sb.toString();
    }
}